package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"

	"github.com/mozillazg/go-cos"
	"github.com/pelletier/go-toml"
)

const DEFAULT_CONFIG = "config.toml"

type config struct {
	SecretId     string `toml:"secretId"`
	SecretKey    string `toml:"secretKey"`
	BucketUrl    string `toml:"bucketUrl"`
	Prefix       string `toml:"prefix"`
	OverrideName string `toml:",omit"`
}

func (c *config) isVaild() bool {
	return c.SecretId != "" && c.SecretKey != "" && c.BucketUrl != ""
}

func parseConfig(data []byte) *config {
	c := new(config)
	toml.Unmarshal(data, c)
	return c
}

func parseConfigFile() *config {
	exeDir := filepath.Dir(os.Args[0])
	confFile := filepath.Join(exeDir, DEFAULT_CONFIG)

	if !isFileExist(confFile) {
		exe, err := os.Executable()
		if err != nil {
			os.Exit(2)
			return nil
		}
		exeDir = filepath.Dir(exe)
		confFile = filepath.Join(exeDir, DEFAULT_CONFIG)
	}

	cf, err := os.Open(confFile)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	defer cf.Close()
	data, err := ioutil.ReadAll(cf)
	if err != nil {
		fmt.Println(err)
		return nil
	}
	return parseConfig(data)
}

func main() {

	var overrideName string
	flag.StringVar(&overrideName, "name", "", "The name to put in bucket, and ignore prefix in config.")
	flag.Parse()

	if flag.NArg() <= 0 {
		fmt.Println("need a target file.")
		os.Exit(1)
		return
	}

	targetFile := flag.Arg(0)

	conf := parseConfigFile()
	// verify config options
	if !conf.isVaild() {
		fmt.Println("config is invalid")
		os.Exit(2)
		return
	}

	u, _ := cos.NewBaseURL(conf.BucketUrl)
	client := cos.NewClient(u, &http.Client{
		Transport: &cos.AuthorizationTransport{
			SecretID:  conf.SecretId,
			SecretKey: conf.SecretKey,
		},
	})

	delegate := newDelegate(client)
	delegate.prefix = conf.Prefix

	err := delegate.uploadFile(targetFile, overrideName)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
		return
	}
}

type qcDelegate struct {
	*cos.Client
	prefix string
}

func newDelegate(client *cos.Client) *qcDelegate {
	return &qcDelegate{
		Client: client,
		prefix: "",
	}
}

func (q *qcDelegate) uploadFile(filename string, name string) error {
	absPath, err := filepath.Abs(filename)
	if err != nil {
		return err
	}
	baseName := filepath.Base(absPath)
	objName := baseName
	if name == "" {
		if q.prefix != "" {
			objName = q.prefix + "/" + baseName
		}
	} else {
		objName = name
	}

	fi, err := os.Stat(absPath)
	if err != nil && err != os.ErrExist {
		return err
	}
	if fi.IsDir() {
		return errors.New("target is a directory")
	}

	opt := &cos.ObjectPutOptions{
		ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
			ContentLength: int(fi.Size()),
		},
	}

	r, err := os.Open(absPath)
	if err != nil {
		return err
	}
	defer r.Close()

	res, err := q.Object.Put(context.Background(), objName, r, opt)
	if err != nil {
		return err
	}
	rid := res.Header.Get("x-cos-request-id")
	fmt.Println(rid)
	return nil
}

func isFileExist(file string) bool {
	info, err := os.Stat(file)
	if info == nil || (err != nil && err != os.ErrExist) {
		return false
	}
	return true
}
